home *** CD-ROM | disk | FTP | other *** search
/ The Guided Tour of Multimedia (Second Edition) / The Guided Tour of Multimedia (Second Edition).iso / trials / qtw111 / inc / qtmacros.inc < prev    next >
Text File  |  1993-04-11  |  33KB  |  739 lines

  1.  
  2.  
  3. ; ---------------------------------------------------------------------
  4. ;
  5. ; QTMACROS.INC - QuickTime for Windows Helper Macros
  6. ;
  7. ;                Version 1.1
  8. ;
  9. ;                (c) 1988-1993 Apple Computer, Inc. All Rights Reserved.
  10. ;
  11. ; ---------------------------------------------------------------------
  12.  
  13.  
  14. COMMENT @
  15. Video dispatch codes
  16. @
  17. VDSP_SETBANK     EQU  1
  18. VDSP_SLIDEWINDOW EQU  2
  19. VDSP_SAVECONTEXT EQU  3
  20. VDSP_RESTCONTEXT EQU  4
  21. VDSP_SETTARGET   EQU  5
  22. VDSP_IDENTIFY    EQU 21
  23. VDSP_VERSION     EQU 22
  24. VDSP_BANKTABLE   EQU 23
  25. VDSP_BITBLTTYPE  EQU 24
  26. VDSP_SCANWIDTH   EQU 25
  27. VDSP_TERMINATE   EQU 86
  28.  
  29. COMMENT @
  30. BitBlt types for hardware support
  31. @
  32. BBL_NONE         EQU  0           ; unknown BitBlt type
  33. BBL_MOVSD        EQU  1           ; use MOVSD
  34. BBL_DRVR         EQU  2           ; use the driver's BitBlt
  35. BBL_MOVPL16      EQU  3           ; 16-bit planar
  36.  
  37. COMMENT @
  38. BMP types
  39.  
  40. These should match the BMP_TYPE enumerated values in QTCODEC.H
  41. @
  42. BMP_NONE         EQU  0           ; unknown type
  43. BMP_DIB          EQU  1           ; DIB
  44. BMP_MONO         EQU  2           ; monochrome
  45. BMP_PACKED_4     EQU  3           ; packed 4 bit, e.g., Fahrenheit
  46. BMP_PLANAR_4     EQU  4           ; VGA or SVGA
  47. BMP_INDEX_8      EQU  5           ; palettized driver
  48. BMP_5_5_5        EQU  6           ; 32,768 colors
  49. BMP_5_6_5        EQU  7           ; XGA, Intel order
  50. BMP_PLANAR_16    EQU  8           ; two planes of one byte each
  51. BMP_8_8_8_RGB    EQU  9           ; true color RGB
  52. BMP_MEMERR       EQU 10           ; insufficient memory for buffers
  53. BMP_8_8_8_BGR    EQU 11           ; true color BGR
  54. BMP_5_6_5_M      EQU 12           ; XGA, Motorola order
  55.  
  56. COMMENT @
  57. Macros AlignBP and AlignBPRet:
  58. Align BP on a DWORD boundary
  59.  
  60. On 386DX and 486 CPUs, applications get better performance by accessing
  61. DWORD operands on DWORD boundaries.  Unaligned operands require an extra
  62. bus cycle.  Our tests on various QuickTime assembler functions shows
  63. unaligned operands typically add a 10% peformance penalty to the entire
  64. function.  By being careful, we can ensure that WORD arguments and WORD
  65. local variables occur in pairs, but we cannot guarantee the alignment of
  66. BP, which is used to access these variables.  The alignment of BP is
  67. determined at run time.  MSC 7.0 aligns the stack only on WORD
  68. boundaries.
  69.  
  70. This macro ensures that BP is aligned on a DWORD boundary.  It is
  71. intended only for use with NEAR PROCs that use the PASCAL calling
  72. sequence.
  73.  
  74. For a NEAR PROC the function arguments and local variables are separated
  75. by two WORDs, the return address and the caller's BP.  Thus, an aligned
  76. BP optimizes accesses to both.  For a FAR PROC the function arguments
  77. and local variables are separated by three WORDs, since the return
  78. address is two WORDs.  In that case, we must define a dummy WORD as the
  79. first local variable and misalign BP, so that the memory references will
  80. be aligned.
  81.  
  82. When BP is not aligned, the macro aligns it, then moves the function
  83. arguments, return address and saved BP accordingly.  The PASCAL
  84. restriction allows the macro to find the byte size of the arguments in
  85. the RET instruction.  The application must insert label "AlignBPRet"
  86. immediately after one of its RET instructions.  The application must
  87. also define a dummy WORD as the last local variable.
  88.  
  89. The macro assumes the direction indicator is 0 (forward), that the
  90. application already saved SI and DI (via USES SI DI on the PROC
  91. statement), and that ES and CX can be destroyed.
  92.  
  93. Upon exit from the function, we must adjust the stack pointer if BP was
  94. changed at entry.  We accomplish this by storing in the extra word made
  95. available at the top of the stack the caller's return address.  At the
  96. location in the stack that normally holds the caller's return address,
  97. we place an address in this program.  At the doctored return address, we
  98. execute a near return.
  99.  
  100. Macro AlignBPExit uses a machine instruction for the near RET to prevent
  101. the assembler from generating epilog code.
  102. @
  103. AlignBP  MACRO
  104.          LOCAL   AlreadyAligned
  105.          TEST    BP, 2            ;; Already on DWORD boundary?
  106.          JE      AlreadyAligned   ;; Skip if already aligned
  107.          CMP     BYTE PTR AlignBPRet - 3, 0C2H ;; Expected RET instruction?
  108.          JNE     AlreadyAligned   ;; Don't adjust if environment unknown
  109.          MOV     CX, WORD PTR [AlignBPRet - 2];; Get byte size of parameter list
  110.          SHR     CX, 1            ;; Convert byte count to word count
  111.          ADD     CX, 2            ;; Account for return address and saved BP
  112.          SUB     BP, 2            ;; Perform the alignment ourselves
  113.                                   ;; Requires a word of slop after all local
  114.                                   ;; variables
  115.          PUSH    SS               ;; Copy SS to ES
  116.          POP     ES               ;;
  117.          MOV     DI, BP           ;; Set target pointer
  118.          LEA     SI, [DI+2]       ;; Set source pointer
  119.          REP     MOVSW [DI],SS:[SI] ;; Move the arguments down
  120.          MOV     AX, [BP+2]       ;; Get caller's return address
  121.          MOV     ES:[DI], AX      ;; Store at top of stack
  122.          MOV     [BP+2], OFFSET AlignFixSP ;; Substitute our return address
  123. AlreadyAligned:
  124.          ENDM
  125.  
  126. AlignBPExit MACRO
  127. AlignBPRet:                       ;; Immediately follow the RETN immed
  128.          ALIGN   16               ;; Align for better performance
  129. AlignFixSP:
  130.          RETN                     ;; Return to caller without epilog
  131.          ENDM
  132.  
  133. COMMENT @
  134. Macro BSWAPX:
  135. Exchange the bytes in a DWORD register.
  136.  
  137. On a 486, the BSWAP instruction uses 1 clock instead of the 6 or 7
  138. clocks required for this macro. Unfortunately, there is no cheap
  139. inline way at run time to distinguish between a 386 and 486.
  140. @
  141. BSWAPX   MACRO   reg:REQ
  142. Root     SUBSTR  <reg>, 2, 1
  143. Xchg8    CATSTR  Root, <X>
  144.          ROL     Xchg8, 8
  145.          ROL     reg, 16
  146.          ROL     Xchg8, 8
  147.          ENDM
  148.  
  149. COMMENT @
  150. Macro to initialize dirty list, initialize bank bounds table, set GS:0
  151. to bank bounds table, set ES:EDI to initial hardware target address,
  152. set the bank for banked adapters.
  153. @
  154. SetHdwTarget MACRO
  155.          LGS     DI, BankBds      ;; Point to bank bounds table
  156.          MOV     DI, GS:[DI]      ;; Get residual block count of first bad row
  157.          MOV     NextBadBlock, DI ;; Save the residual block count to match
  158.  
  159.          MOV     AX, WORD PTR OffScreen ;; Point to dirty list
  160.          REPEAT  BPPT * Mag
  161.          ADD     AX, BWIDTH       ;; Allocate room for offscreen pixels
  162.          ENDM                     ;; of REPEAT  BPPT * Mag
  163.          IF      Mag EQ 2
  164.          ADD     AX, WdBytes      ;; Allocate room for second scan line in pair
  165.          ENDIF
  166.          INC     AX               ;; Round up to even offset for efficiency
  167.          AND     AL, 0FEH         ;;
  168.          ADD     AX, 4            ;; Leave room for post-processing
  169.          MOV     DirtyListFirst, AX ;;
  170.  
  171.          MOV     AX, DESTY        ;; Get starting row
  172.          MUL     WdBytes          ;; Multiply by width of adapter scan line
  173.          REPEAT  BPPT             ;; Bytes per target pixel
  174.          ADD     AX, DESTX        ;; Add in destination column
  175.          ADC     DX, 0            ;; Bump bank number if carry
  176.          ENDM                     ;; of REPEAT BPPT
  177.          PUSH    DX               ;; Push high order word
  178.          PUSH    AX               ;; Push low order word
  179.          PUSH    VDSP_SETTARGET   ;; Dispatch code for SetTarget
  180.          CALL    [Hardware]       ;; Set the initial bank, also ES:DI
  181.          ADD     SP, 6            ;; Remove arguments from the stack
  182.          MOV     Bank, AX         ;; Save the returned bank
  183.          MOV     SelVRAM, ES      ;; Save VRAM selector
  184.          ENDM                     ;; of SetHdwTarget MACRO
  185.  
  186. COMMENT @
  187. Bank change macros:
  188. Used by functions that write directly to video adapter hardware.
  189.  
  190. The bank change macros assume that all bank changes are of size 1.  This
  191. is so, since the program moves at most 4 rows at a time and each bank holds
  192. at least 40 rows.
  193. @
  194. DoBankUp MACRO
  195.          INC     Bank             ;; Go to next bank
  196.          PUSH    Bank             ;; Pass argument to function
  197.          PUSH    VDSP_SETBANK     ;; Dispatch code for SetBank
  198.          CALL    [Hardware]       ;; Set the new bank
  199.          ADD     SP, 4            ;; Remove arguments from the stack
  200.          ENDM
  201.  
  202. CheckBankUp MACRO  nbr:REQ        ;; Check bank, jumping only when the bank
  203.          JC      NewBankUp&nbr&   ;;  changes
  204. NewBankUp&nbr&Ret:
  205.          ENDM
  206.  
  207. SetBankUp MACRO  nbr:REQ          ;; Effect a bank change
  208.          ALIGN   16
  209. NewBankUp&nbr&:
  210.          DoBankUp
  211.          JMP     NewBankUp&nbr&Ret;; Return to main code
  212.          ENDM
  213.  
  214. DoBankDn MACRO
  215.          DEC     Bank             ;; Go to previous bank
  216.          PUSH    Bank             ;; Pass argument to function
  217.          PUSH    VDSP_SETBANK     ;; Dispatch code for SetBank
  218.          CALL    [Hardware]       ;; Set the new bank
  219.          ADD     SP, 4            ;; Remove arguments from the stack
  220.          ENDM
  221.  
  222. CheckBankDn MACRO  nbr:REQ        ;; Check bank, jumping only when the bank
  223.          JC      NewBankDn&nbr&   ;;  changes
  224. NewBankDn&nbr&Ret:
  225.          ENDM
  226.  
  227. SetBankDn MACRO  nbr:REQ          ;; Effect a bank change
  228.          ALIGN   16
  229. NewBankDn&nbr&:
  230.          DoBankDn
  231.          JMP     NewBankDn&nbr&Ret;; Return to main code
  232.          ENDM
  233.  
  234. SlideWindow MACRO
  235.          PUSH    Bank             ;; SlideWindow may adjust this
  236.          PUSH    VDSP_SLIDEWINDOW ;; Dispatch code for SlideWindow
  237.          CALL    [Hardware]       ;; Call the function
  238.          ADD     SP, 4            ;; Remove arguments from the stack
  239.                                   ;; AX = bank, ES:DI may be changed
  240.          MOV     Bank, AX         ;; Bank number may have changed
  241.          ENDM
  242.  
  243. COMMENT @
  244. Macros used with downward dithers from 24 bit sources
  245.  
  246. Get554 is used with 8 bit targets.  The result is placed in BX, where it
  247. can then be used as an index into a dither table.
  248.  
  249. Get555 is used with 15 bit (5-5-5) targets.
  250.  
  251. Get565 and Get565M are used with 16 bit (5-6-5) targets.
  252. @
  253. Get554   MACRO   offst:REQ             ;;
  254.          MOV     BH, [ESI+offst]       ;; Get R
  255.          SHR     BH, 3                 ;; Save 5 bits of R
  256.          MOV     BL, [ESI+offst+1]     ;; Get G
  257.          SHL     EBX, 5                ;; Save 5 bits of G
  258.          MOV     BL, [ESI+offst+2]     ;; Get B
  259.          SHR     EBX, 4                ;; Get 5-5-4
  260.          ENDM
  261.  
  262. Get555   MACRO   offst:REQ
  263.          MOV     AH, [ESI+offst]       ;; Get R
  264.          SHR     AH, 3                 ;; Save 5 bits of R
  265.          MOV     AL, [ESI+offst+1]     ;; Get G
  266.          SHL     EAX, 5                ;; Save 5 bits of G
  267.          MOV     AL, [ESI+offst+2]     ;; Get B
  268.          SHR     EAX, 3                ;; Get 5-5-5
  269.          ENDM
  270.  
  271. Get565   MACRO   offst:REQ
  272.          MOV     AH, [ESI+offst]       ;; Get R
  273.          SHR     AH, 3                 ;; Save 5 bits of R
  274.          MOV     AL, [ESI+offst+1]     ;; Get G
  275.          SHL     EAX, 6                ;; Save 6 bits of G
  276.          MOV     AL, [ESI+offst+2]     ;; Get B
  277.          SHR     EAX, 3                ;; Get 5-6-5
  278.          ENDM
  279.  
  280. Get565M  MACRO   offst:REQ
  281.          Get565  offst                 ;; Get 5-6-5 in Intel order
  282.          ROL     AX, 8                 ;; Put bytes in Motorola order
  283.          ENDM
  284.  
  285. COMMENT @
  286. Utility macros
  287.  
  288. The macro assumes CX bytes will be moved from DS:SI to ES:DI, and that
  289. DX is available.
  290. @
  291. @Equals  MACRO   p1:REQ, p2:REQ
  292.          EXITM   %( @InStr(,p1,p2) * @InStr(,p2,p1))
  293.          ENDM                     ;; of @Equals MACRO
  294.  
  295. CopyBytes MACRO
  296.          MOV     DX, CX           ;; Save byte count
  297.          SHR     CX, 2            ;; Byte count to DWORD count
  298.          REP     MOVSD            ;; Copy most of the bytes
  299.          MOV     CX, DX           ;; Restore byte count
  300.          AND     CX, 3            ;; 0-3 bytes left
  301.          SHR     CX, 1            ;; Check for 2 or more bytes left
  302.          REP     MOVSW            ;; Copy stray WORD, if any
  303.          ADC     CX, CX           ;; 0-1 bytes left
  304.          REP     MOVSB            ;; Copy stray byte, if any
  305.          ENDM                     ;; of CopyBytes MACRO
  306.  
  307. COMMENT @
  308. Macros used with decompressors that write directly to video adapter
  309. hardware.
  310. @
  311. GoOnScreenX MACRO
  312.          ALIGN   16
  313. GoOnScreen:
  314.          PUSH    CX               ;; Save register
  315.          MOV     CX, DirtyListNext     ;; Offset of next dirty list entry
  316.          SUB     CX, DirtyListFirst    ;; 4 * number of dirty list entries
  317.          JLE     NotDirty         ;; Skip if dirty list is empty
  318.  
  319.          PUSH    BX               ;; Save registers
  320.          PUSH    DX               ;;
  321.          PUSH    SI               ;;
  322.          PUSH    DI               ;;
  323.          PUSH    DS               ;;
  324.          PUSH    ES               ;;
  325.  
  326.          SHR     CX, 2            ;; Number of dirty list entries
  327.          MOV     DX, BadStart     ;; Onscreen offset of line start
  328.          NEG     DX               ;; Offset in offscreen buffer of bank split
  329.                                   ;; Merge adjacent ranges
  330.                                   ;; Split a range that splits a bank
  331.          MOV     DS, WORD PTR OffScreen +2  ;; Offscreen buffer is the source
  332.          MOV     SI, DirtyListFirst         ;; Point to first dirty list entry
  333.          LEA     DI, [SI-4]                 ;; Allow room for one split entry
  334. LoopNewRange:
  335.          MOV     AX, [SI]         ;; Get start of range
  336.          MOV     [DI], AX         ;; Save start of range
  337. LoopSameRange:
  338.          MOV     BX, [SI+2]       ;; Get end of range
  339.          ADD     SI, 4            ;; Point to next source slot
  340.          DEC     CX               ;; One fewer source slot
  341.          JLE     RangeDone        ;; Skip if no more source slots
  342.          CMP     BX, [SI]         ;; Is the next range adjacent to this?
  343.          JB      RangeDone        ;; Skip if ranges are not adjacent
  344.          JMP     LoopSameRange    ;; Keep combining adjacent ranges
  345. RangeDone:
  346.          CMP     AX, DX           ;; Might split occur in this range?
  347.          JGE     NoSplit          ;; Skip if split does not occur in this range
  348.          CMP     BX, DX           ;; Does split occur in this range?
  349.          JLE     NoSplit          ;; Skip if split does not occur in this range
  350.          MOV     [DI+2], DX       ;;
  351.          ADD     DI, 4            ;; Point to next target slot
  352.          MOV     [DI], DX         ;; Start of new range
  353. NoSplit:
  354.          MOV     [DI+2], BX       ;; Set right edge of range
  355.          ADD     DI, 4            ;; Point to next target slot
  356.          TEST    CX, CX           ;; Any source slots left?
  357.          JG      LoopNewRange     ;; Loop while source slots remain
  358.          OR      WORD PTR [DI], -1;; Terminate the list
  359.                                   ;; Make offsets relative to prior entries
  360.                                   ;; Convert ending offsets to byte sizes
  361.          LEA     CX, [DI+4]       ;; Compute number of list entries
  362.          SUB     CX, DirtyListFirst    ;; 4 * number of WORDs in the list
  363.          LEA     BX, [DI-2]       ;; Point to last WORD in list
  364.          SHR     CX, 1            ;; 2 * number of WORDs in list
  365.          DEC     CX               ;; Don't process first WORD
  366. LoopDiff:
  367.          MOV     AX, [BX-2]       ;; Get previous WORD in list
  368.          SUB     [BX], AX         ;; Compute difference from previous WORD
  369.          SUB     BX, 2            ;; Back up one WORD
  370.          DEC     CX               ;; One fewer WORD
  371.          JG      LoopDiff         ;; Loop once for each WORD in list
  372.                                   ;; Copy the first line of the pair
  373.          MOV     ES, SelVRAM      ;; VRAM is the target
  374.          MOV     DI, BadStart     ;;
  375.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  376. LoopCopy:
  377.          MOV     AX, [BX]         ;; Get offset of range start
  378.          TEST    AX, AX           ;; End of the list?
  379.          JL      DirtyDone        ;; Skip if end of the list
  380.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  381.          ADD     BX, 4            ;; Point to next list entry
  382.          ADD     SI, AX           ;; Locate data in offscreen buffer
  383.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  384.          CheckBankUp 01E          ;; Change bank if necessary
  385.          CopyBytes                ;; Copy the bytes
  386.          TEST    DI, DI           ;; Did range end on a bank boundary?
  387.          JZ      NewBankUp02E     ;; Skip if range ended on a bank boundary
  388. NewBankUp02ERet:
  389.          JMP     LoopCopy         ;; Loop once for each range
  390.  
  391.          ALIGN   16
  392. DirtyDone:
  393.          TEST    DI, DI           ;; Did we cross the bank boundary?
  394.          JGE     DirtyCrossed     ;; Skip if we already crossed
  395.          DoBankUp                 ;; Cross the bank boundary
  396. DirtyCrossed:
  397.          POP     ES               ;; Restore registers
  398.          POP     DS               ;;
  399.          POP     DI               ;;
  400.          POP     SI               ;;
  401.          POP     DX               ;;
  402.          POP     BX               ;;
  403.          POP     CX               ;;
  404.          RETN                     ;; Return to caller without epilog
  405.  
  406.          ALIGN   16
  407. NotDirty:
  408.          POP     CX               ;; Restore register
  409.          DoBankUp                 ;; Go to next bank
  410.          RETN                     ;; Return to caller without epilog
  411.          ENDM                     ;; of GoOnScreenX
  412.  
  413. GoOnScreen2x MACRO
  414.          ALIGN   16
  415. GoOnScreen:
  416.          PUSH    CX               ;; Save register
  417.          MOV     CX, DirtyListNext     ;; Offset of next dirty list entry
  418.          SUB     CX, DirtyListFirst    ;; 4 * number of dirty list entries
  419.          JLE     NotDirty         ;; Skip if dirty list is empty
  420.  
  421.          PUSH    BX               ;; Save registers
  422.          PUSH    DX               ;;
  423.          PUSH    SI               ;;
  424.          PUSH    DI               ;;
  425.          PUSH    DS               ;;
  426.          PUSH    ES               ;;
  427.  
  428.          SHR     CX, 2            ;; Number of dirty list entries
  429.          MOV     BX, BadStart     ;; Onscreen offset of line start
  430.          MOV     DX, BX           ;; Copy onscreen offset of line start
  431.          NEG     DX               ;; Offset in offscreen buffer of bank split?
  432.          ADD     BX, WdBytes      ;; Onscreen offset of second line in pair
  433.          JC      FirstLineSplit   ;; Skip if first line in pair has bank split
  434.          MOV     DX, BX           ;; Copy onscreen offset of line start
  435.          NEG     DX               ;; Offset in offscreen buffer of bank split
  436. FirstLineSplit:
  437.                                   ;; Merge adjacent ranges
  438.                                   ;; Split a range that splits a bank
  439.          MOV     DS, WORD PTR OffScreen +2  ;; Offscreen buffer is the source
  440.          MOV     SI, DirtyListFirst         ;; Point to first dirty list entry
  441.          LEA     DI, [SI-4]                 ;; Allow room for one split entry
  442. LoopNewRange:
  443.          MOV     AX, [SI]         ;; Get start of range
  444.          MOV     [DI], AX         ;; Save start of range
  445. LoopSameRange:
  446.          MOV     BX, [SI+2]       ;; Get end of range
  447.          ADD     SI, 4            ;; Point to next source slot
  448.          DEC     CX               ;; One fewer source slot
  449.          JLE     RangeDone        ;; Skip if no more source slots
  450.          CMP     BX, [SI]         ;; Is the next range adjacent to this?
  451.          JB      RangeDone        ;; Skip if ranges are not adjacent
  452.          JMP     LoopSameRange    ;; Keep combining adjacent ranges
  453. RangeDone:
  454.          CMP     AX, DX           ;; Might split occur in this range?
  455.          JGE     NoSplit          ;; Skip if split does not occur in this range
  456.          CMP     BX, DX           ;; Does split occur in this range?
  457.          JLE     NoSplit          ;; Skip if split does not occur in this range
  458.          MOV     [DI+2], DX       ;;
  459.          ADD     DI, 4            ;; Point to next target slot
  460.          MOV     [DI], DX         ;; Start of new range
  461. NoSplit:
  462.          MOV     [DI+2], BX       ;; Set right edge of range
  463.          ADD     DI, 4            ;; Point to next target slot
  464.          TEST    CX, CX           ;; Any source slots left?
  465.          JG      LoopNewRange     ;; Loop while source slots remain
  466.                                   ;;
  467.          OR      WORD PTR [DI], -1;; Terminate the list
  468.                                   ;; Make offsets relative to prior entries
  469.                                   ;; Convert ending offsets to byte sizes
  470.          LEA     CX, [DI+4]       ;; Compute number of list entries
  471.          SUB     CX, DirtyListFirst    ;; 4 * number of WORDs in the list
  472.          LEA     BX, [DI-2]       ;; Point to last WORD in list
  473.          SHR     CX, 1            ;; 2 * number of WORDs in list
  474.          DEC     CX               ;; Don't process first WORD
  475. LoopDiff:
  476.          MOV     AX, [BX-2]       ;; Get previous WORD in list
  477.          SUB     [BX], AX         ;; Compute difference from previous WORD
  478.          SUB     BX, 2            ;; Back up one WORD
  479.          DEC     CX               ;; One fewer WORD
  480.          JG      LoopDiff         ;; Loop once for each WORD in list
  481.  
  482.                                   ;; Copy the first line of the pair
  483.          MOV     ES, SelVRAM      ;; VRAM is the target
  484.          MOV     DI, BadStart     ;;
  485.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  486. LoopCopy1:
  487.          MOV     AX, [BX]         ;; Get offset of range start
  488.          TEST    AX, AX           ;; End of the list?
  489.          JL      DirtyDone1       ;; Skip if end of the list
  490.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  491.          ADD     BX, 4            ;; Point to next list entry
  492.          ADD     SI, AX           ;; Locate data in offscreen buffer
  493.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  494.          CheckBankUp 01E          ;; Change bank if necessary
  495.          CopyBytes                ;; Copy the bytes
  496.          TEST    DI, DI           ;; Did range end on a bank boundary?
  497.          JZ      NewBankUp02E     ;; Skip if range ended on a bank boundary
  498. NewBankUp02ERet:
  499.          JMP     LoopCopy1        ;; Loop once for each range
  500.  
  501.          ALIGN   16
  502.                                   ;; Copy the second line of the pair
  503. DirtyDone1:
  504.          MOV     AX, BadStart     ;; Onscreen offset of first line in pair
  505.          ADD     AX, WdBytes      ;; Onscreen offset of second line in pair
  506.          SUB     AX, DI           ;; Offset from current target pointer
  507.          ADD     DI, AX           ;; Add the offset back in
  508.          CheckBankUp 03E          ;; Change bank if necessary
  509.          MOV     SI, WORD PTR OffScreen     ;; Offscreen buffer is the source
  510.          MOV     BX, DirtyListFirst         ;; Point to first dirty list entry
  511.          SUB     BX, 4                      ;; Post-processed first entry
  512. LoopCopy2:
  513.          MOV     AX, [BX]         ;; Get offset of range start
  514.          TEST    AX, AX           ;; End of the list?
  515.          JL      DirtyDone2       ;; Skip if end of the list
  516.          MOV     CX, [BX+2]       ;; Get number of bytes in the range
  517.          ADD     BX, 4            ;; Point to next list entry
  518.          ADD     SI, AX           ;; Locate data in offscreen buffer
  519.          ADD     DI, AX           ;; Position pointer in onscreen buffer
  520.          CheckBankUp 04E          ;; Change bank if necessary
  521.          CopyBytes                ;; Copy the bytes
  522.          TEST    DI, DI           ;; Did range end on a bank boundary?
  523.          JZ      NewBankUp05E     ;; Skip if range ended on a bank boundary
  524. NewBankUp05ERet:
  525.          JMP     LoopCopy2        ;; Loop once for each range
  526.  
  527.          ALIGN   16
  528. DirtyDone2:
  529.          TEST    DI, DI           ;; Did we cross the bank boundary?
  530.          JGE     DirtyCrossed2    ;; Skip if we already crossed
  531.          DoBankUp                 ;; Cross the bank boundary
  532. DirtyCrossed2:
  533.          POP     ES               ;; Restore registers
  534.          POP     DS               ;;
  535.          POP     DI               ;;
  536.          POP     SI               ;;
  537.          POP     DX               ;;
  538.          POP     BX               ;;
  539.          POP     CX               ;;
  540.          RETN                     ;; Return to caller without epilog
  541.  
  542.          ALIGN   16
  543. NotDirty:
  544.          POP     CX               ;; Restore register
  545.          DoBankUp                 ;; Go to next bank
  546.          RETN                     ;; Return to caller without epilog
  547.          ENDM                     ;; of GoOnScreen2x
  548.  
  549. COMMENT @
  550. Macro used to move pixels from onscreen buffer to offscreen buffer.
  551. @
  552. GoOffScreenX MACRO
  553.          LOCAL   NewBank
  554.          LOCAL   AllFits
  555.          LOCAL   ExitPath
  556.          ALIGN   16
  557. GoOffScreen:
  558.          PUSH    DS               ;; Save register
  559.          PUSH    CX               ;; Save register
  560.          PUSH    DX               ;; Save register
  561.          PUSH    SI               ;; Save register
  562.          PUSH    DI               ;; Save register
  563.          MOV     CX, AX           ;; Get byte count
  564.          MOV     DS, SelVRAM      ;; Point to onscreen buffer
  565.          MOV     SI, DI           ;; Copy target offset
  566.          SUB     SI, WORD PTR OffScreen ;; Compute offset from start of buffer
  567.          ADD     SI, BadStart     ;; Index into onscreen buffer
  568.          JC      NewBank          ;; Skip if we crossed a bank boundary
  569.          ADD     SI, CX           ;; Check ending source offset
  570.          JNC     AllFits          ;; Skip if no bank change required
  571.          JZ      AllFits          ;; Skip if no bank change required
  572.          PUSH    SI               ;; Save byte count for new bank
  573.          SUB     SI, CX           ;; Restore source pointer
  574.          MOV     CX, SI           ;; Compute byte count for old bank
  575.          NEG     CX               ;;
  576.          CopyBytes                ;; Copy the bytes
  577.          POP     CX               ;; Get byte count for new bank
  578. NewBank:
  579.          DoBankUp                 ;; Go to new bank
  580.          CopyBytes                ;; Copy the bytes
  581.          DoBankDn                 ;; Back to old bank
  582. ExitPath:
  583.          POP     DI               ;; Restore register
  584.          POP     SI               ;; Restore register
  585.          POP     DX               ;; Restore register
  586.          POP     CX               ;; Restore register
  587.          POP     DS               ;; Restore register
  588.          RETN                     ;; Return to caller without epilog
  589.  
  590.          ALIGN   16
  591. AllFits:                          ;; Source does not cross a bank boundary
  592.          SUB     SI, CX           ;; Restore source pointer
  593.          CopyBytes                ;; Copy the bytes
  594.          JMP     ExitPath         ;; Go to common exit
  595.          ENDM
  596.  
  597. COMMENT @
  598. Macro to compute number of source bytes processed, used for BMP banding
  599. @
  600. BytesUsed MACRO
  601.          XOR     EAX, EAX           ;; Zero out high order word
  602.          MOV     AX, WORD PTR Inbuf ;; Get offset of source pointer
  603.          SUB     ESI, EAX           ;; Compute number of source bytes processed
  604.          SHLD    EDX, ESI, 16       ;; Put high order word in DX
  605.          MOV     AX, SI             ;; Put low order word in AX
  606.          ENDM
  607.  
  608. COMMENT @
  609. Macros for moving pixels to and from overscan area
  610.  
  611. Note that the 2X versions of the macros should not be used with 8-bit
  612. targets since dithering will produce different target values for the
  613. same source.
  614.  
  615. We generate error messages only in the SavePixels macro, figuring they
  616. would be superfluous in RestPixels.
  617. @
  618. SaveOneRow MACRO
  619. j        =       0
  620.          REPEAT  Limit
  621.          MOV     EAX, ES:[DI+j]   ;; Get 4 bytes
  622.          MOV     OverScan[i], EAX ;; Save the bytes
  623. i        =       i + 4
  624. j        =       j + 4
  625.          ENDM                     ;; of REPEAT Limit
  626.          ENDM
  627.  
  628. RestOneRow MACRO Width:REQ
  629. j        =       0
  630.          WHILE   j  LT  Width - Width MOD 4
  631.          MOV     EAX, OverScan[i] ;; Get 4 bytes
  632.          MOV     ES:[DI+j], EAX   ;; Restore the bytes
  633. i        =       i + 4
  634. j        =       j + 4
  635.          ENDM                     ;; of WHILE  j  LT  Width - Width MOD 4
  636.          IF      j  LT  Width - Width MOD 2
  637.          MOV     AX, WORD PTR OverScan[i]   ;; Get 2 bytes
  638.          MOV     ES:[DI+j], AX              ;; Restore the bytes
  639. i        =       i + 2
  640. j        =       j + 2
  641.          ENDIF                    ;; of IF      j  LT  Width - Width MOD 2
  642.          IF      j  LT  Width
  643.          MOV     AL, BYTE PTR OverScan[i]   ;; Get 1 byte
  644.          MOV     ES:[DI+j], AL              ;; Restore the byte
  645. i        =       i + 1
  646. j        =       j + 1
  647.          ENDIF                    ;; of IF      j  LT  Width
  648. i        =       (i + 3) AND 0FFFCH    ;; Round up to multiple of 4
  649.          ENDM                     ;; of RestOneRow MACRO
  650.  
  651. SavePixels MACRO Width:REQ, Height:REQ, Increment:REQ
  652. i        =       0
  653. Limit    =       (((Width) + 3) AND 0FFFCH) / 4
  654. ReqdSize TEXTEQU %(Limit * Height)
  655.          IFNDEF  OverScan
  656. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  657.          EXITM
  658.          ENDIF
  659.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  660. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  661.          EXITM
  662.          ENDIF
  663.          REPEAT  Height
  664.          ADD     DI, Increment
  665.          SaveOneRow
  666.          ENDM                     ;; of REPEAT Height
  667.          ENDM                     ;; of MACRO
  668.  
  669. RestPixels MACRO Width:REQ, Height:REQ, Increment:REQ
  670. i        =       0
  671. Limit    =       (((Width) + 3) AND 0FFFCH) / 4
  672. ReqdSize TEXTEQU %(Limit * Height)
  673.          IFNDEF  OverScan         ;; If variable not defined
  674.          EXITM                    ;; Avoid further error messages
  675.          ENDIF
  676.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  677.          EXITM
  678.          ENDIF
  679.          REPEAT  Height
  680.          ADD     DI, Increment
  681.          RestOneRow (Width)
  682.          ENDM                     ;; of REPEAT Height
  683.          ENDM                     ;; of MACRO
  684.  
  685. SavePix2X MACRO Width:REQ, Height:REQ, Increment:REQ
  686. i        =       0
  687. Limit    =       (((Width) * 2 + 3) AND 0FFFCH) / 4
  688. ReqdSize TEXTEQU %(Limit * Height)
  689.          IFNDEF  OverScan
  690. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  691.          EXITM
  692.          ENDIF
  693.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  694. %       .ERR     <Define OverScan[ReqdSize]:DWORD>
  695.          EXITM
  696.          ENDIF
  697.          REPEAT  Height
  698.          ADD     DI, Increment
  699.          ADD     DI, Increment
  700.          SaveOneRow
  701.          ENDM                     ;; of REPEAT Height
  702.          ENDM                     ;; of MACRO
  703.  
  704. RestPix2X MACRO Width:REQ, Height:REQ, Increment:REQ
  705. i        =       0
  706. Limit    =       (((Width) * 2 + 3) AND 0FFFCH) / 4
  707. ReqdSize TEXTEQU %(Limit * Height)
  708.          IFNDEF  OverScan         ;; If variable not defined
  709.          EXITM                    ;; Avoid further error messages
  710.          ENDIF
  711.          IF      ( TYPE OverScan  NE  4)  OR  (ReqdSize  NE  LENGTHOF OverScan)
  712.          EXITM
  713.          ENDIF
  714.          PUSH    BX               ;; Save register
  715.          MOV     BX, Increment    ;; Initialize index register
  716. j        =       0
  717.          REPEAT  Limit            ;; Restore row #2 from row #1
  718.          MOV     EAX, ES:[DI+j]   ;; Get 4 uncorrupted bytes
  719.          MOV     ES:[DI+BX+j], EAX;; Restore the bytes
  720. j        =       j + 4
  721.          ENDM                     ;; of REPEAT Limit
  722.          REPEAT  Height - 1
  723.          ADD     DI, BX
  724.          ADD     DI, BX
  725. j        =       0
  726.          REPEAT  Limit
  727.          MOV     EAX, OverScan[i] ;; Get 4 saved bytes
  728.          MOV     ES:[DI+j], EAX   ;; Restore the bytes to row #1
  729.          MOV     ES:[DI+BX+j], EAX;; Restore the bytes to row #2
  730. i        =       i + 4
  731. j        =       j + 4
  732.          ENDM                     ;; of REPEAT Limit
  733.          ENDM                     ;; of REPEAT Height
  734.          ADD     DI, BX           ;; Point to last row
  735.          ADD     DI, BX
  736.          RestOneRow ((Width) * 2)
  737.          POP     BX               ;; Restore register
  738.          ENDM                     ;; of MACRO
  739.